package com.cke.yang.security.app.service.impl;

import com.cke.yang.common.infra.utils.UserDetailsUtils;
import com.cke.yang.security.api.dto.response.SecMenuNodeResponse;
import com.cke.yang.security.app.service.SecMenuService;
import com.cke.yang.security.domain.entity.SecMenu;
import com.cke.yang.security.domain.repository.SecMenuRepository;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * 菜单服务实现类
 *
 * @author yuyang
 */
@Slf4j
@Service
public class SecMenuServiceImpl implements SecMenuService {

    @Autowired
    private SecMenuRepository secMenuRepository;

    @Override
    public List<SecMenuNodeResponse> getCurrentRoleMenus() {
        log.debug("获取当前用户的当前角色的菜单列表");

        // 获取当前登录用户的当前角色ID
        Long currentRoleId = UserDetailsUtils.getCurrentRoleId();
        if (currentRoleId == null) {
            log.warn("当前用户未登录或未设置当前角色，返回空菜单列表");
            return List.of();
        }

        log.debug("当前用户的当前角色ID: {}", currentRoleId);

        // 通过Repository获取角色菜单列表
        List<SecMenu> menus = secMenuRepository.getMenuListByRoleId(currentRoleId);
        /**
         * 菜单分为根菜单和子菜单，菜单的父子关系是通过菜单的 menuPath 字段来判断的
         * 比如：根菜单menuCode=a的menuPath=a，其下子菜单menuCode=a1的menuPath就为a.a1
         * 分配关系表里只分配子菜单给角色，因此此处需要根据这些子菜单的menuPath，以英文句号分隔，拿到根菜单的menuCode，然后去数据库取出跟菜单
         */

        if (menus.isEmpty()) {
            log.debug("当前角色菜单数量: 0");
            return List.of();
        }
        
        // 1. 收集所有需要的根菜单编码
        Set<String> rootMenuCodes = collectRootMenuCodes(menus);

        // 2. 批量查询所有根菜单（优化：一次性查询所有根菜单，避免多次 数据库查询）
        List<SecMenu> rootMenus = secMenuRepository.getMenuListByMenuCodes(rootMenuCodes);
        log.debug("需要查询的根菜单编码: {}, 实际查询到的根菜单数量: {}", rootMenuCodes, rootMenus.size());

        // 3. 构建菜单树结构
        ArrayList<SecMenuNodeResponse> result = getSecMenuNodeResponses(rootMenus, menus);

        log.debug("当前角色菜单数量: {}", menus.size());
        log.debug("构建的根菜单数量: {}", result.size());
        // 4. 倒序;
        Collections.reverse(result);
        return result;
    }

    private static ArrayList<SecMenuNodeResponse> getSecMenuNodeResponses(List<SecMenu> rootMenus, List<SecMenu> menus) {
        Map<String, SecMenuNodeResponse> rootNodeMap = new HashMap<>(rootMenus.size());

        // 先创建所有根节点
        for (SecMenu rootMenu : rootMenus) {
            SecMenuNodeResponse rootNode = new SecMenuNodeResponse(rootMenu);
            rootNodeMap.put(rootMenu.getMenuCode(), rootNode);
        }

        // 4. 将子菜单添加到对应的根菜单下
        for (SecMenu menu : menus) {
            String menuPath = menu.getMenuPath();
            if (menuPath != null && menuPath.contains(".")) {
                // 这是子菜单，找到对应的根菜单
                String rootMenuCode = menuPath.split("\\.")[0];
                SecMenuNodeResponse rootNode = rootNodeMap.get(rootMenuCode);
                if (rootNode != null) {
                    SecMenuNodeResponse childNode = new SecMenuNodeResponse(menu);
                    rootNode.addChildNode(childNode);
                }
            }
        }

        // 5. 返回结果列表
        return new ArrayList<>(rootNodeMap.values());
    }

    /**
     * 收集所有需要的根菜单编码
     *
     * @param menus 菜单列表
     * @return 根菜单编码集合
     */
    private Set<String> collectRootMenuCodes(List<SecMenu> menus) {
        Set<String> rootMenuCodes = new HashSet<>();
        for (SecMenu menu : menus) {
            String menuPath = menu.getMenuPath();
            if (menuPath != null && menuPath.contains(".")) {
                // 子菜单，提取根菜单编码（第一个点之前的部分）
                String rootMenuCode = menuPath.split("\\.")[0];
                rootMenuCodes.add(rootMenuCode);
            } else {
                // 本身就是根菜单
                rootMenuCodes.add(menu.getMenuCode());
            }
        }
        return rootMenuCodes;
    }

}

